package com.packer.walle;

import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ApkV1Utils{
    private ApkV1Utils(){
    }

    public static byte[] readZipCommentByMagic(File file, String magicStr){
        byte[] magic = magicStr.getBytes();
        try(RandomAccessFile raf = new RandomAccessFile(file, "r")){
            long index = raf.length() - magic.length - 2;
            raf.seek(index);
            // read content length field
            byte[] bytes = new byte[2];
            raf.readFully(bytes);
            int length = ByteBuffer.wrap(bytes)
                    .order(ByteOrder.LITTLE_ENDIAN)
                    .getShort(0);
            if(length <= 0){
                return null;
            }
            index = index - length;
            raf.seek(index);
            // read content bytes
            byte[] commentByte = new byte[length];
            raf.readFully(commentByte);
            return commentByte;
        }catch(Throwable ignored){
        }
        return null;
    }

    /**
     * @param targetApk
     * @param magicStr
     * @param commentBytes 要写入的字符串转byte
     * @throws IOException
     */
    public static void writeComment(File targetApk, String magicStr, byte[] commentBytes) throws IOException{
        byte[] magic = magicStr.getBytes();
        if(hasMagic(targetApk, magic)){
            byte[] readCommentBytes = readZipCommentByMagic(targetApk, magicStr);
            if(isEquals(commentBytes, readCommentBytes)){
                String message = targetApk + "->Already exists comment：" + new String(readCommentBytes);
                throw new IOException(message);
            }
        }
        // {@see java.util.zip.ZipOutputStream.writeEND}
        try(RandomAccessFile raf = new RandomAccessFile(targetApk, "rw")){
            raf.seek(targetApk.length() - 2);
            // write zip comment length
            // (content field length + length field length + magic field length)
            writeShort(commentBytes.length + 2 + magic.length, raf);
            // write content
            raf.write(commentBytes);
            // write content length
            writeShort(commentBytes.length, raf);
            // write magic bytes
            raf.write(magic);
        }
    }

    private static void writeShort(int i, DataOutput out) throws IOException{
        ByteBuffer byteBuffer = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
        byteBuffer.putShort((short) i);
        out.write(byteBuffer.array());
    }

    public static boolean hasMagic(File file, byte[] magic){
        try(RandomAccessFile raf = new RandomAccessFile(file, "r")){
            byte[] buffer = new byte[magic.length];
            long index = raf.length() - magic.length;
            // read magic bytes
            raf.seek(index);
            raf.readFully(buffer);
            // check magic bytes matched
            return isEquals(buffer, magic);
        }catch(Throwable e){
            return false;
        }
    }

    public static boolean isEquals(byte[] b1, byte[] b2){
        if(b1 == null || b2 == null){
            return false;
        }

        if(b1 == b2){
            return true;
        }

        if(b1.length != b2.length){
            return false;
        }

        for(int i = 0; i < b1.length; i++){
            if(b1[i] != b2[i]){
                return false;
            }
        }
        return true;
    }
}